#include <common/logger/logger.h>
#include <filesystem>
#include <fileutil/fileutil_p.h>
#include <fstream>
#include <iostream>
#include <regex>
#include <zip.h>

#include <QDir>
#include <QFileIconProvider>
#include <QFileInfo>
#include <QTemporaryFile>
#include <QUrl>
const QSize ICON_SIZE(64, 64);
std::mutex FileUtilPrivate::m_fileLocker;
/*******核心逻辑********
 * 压缩单一文件，直接放入zip包中压缩完成
 * 压缩目录，递归归档，创建zip包
 */
bool FileUtilPrivate::zipFile(const std::string& srcPath, const std::string& destPath)
{
	fs::path src(srcPath);
	fs::path dest(destPath);

	if (!fs::exists(src))
	{
		LOG_ERROR("源路径不存在：", srcPath);
		return false;
	}

	try
	{
		if (fs::is_regular_file(src))
		{
			CompressFile(src, dest);
		}
		else if (fs::is_directory(src))
		{
			CompressDirectory(src, dest);
		}
		else
		{
			LOG_ERROR("不支持的源路径类型：", srcPath);
			return false;
		}
	}
	catch (const std::exception& e)
	{
		LOG_ERROR("压缩失败： ", e.what());
		return false;
	}

	LOG_DEBUG("压缩成功，源路径:", srcPath, "，压缩文件输出路径：", destPath);
	return true;
}

bool FileUtilPrivate::unZipFile(const std::string& srcPath, const std::string& destPath)
{
	fs::path src(srcPath);
	fs::path dest(destPath);

	if (!fs::exists(src))
	{
		LOG_ERROR("源路径不存在：", srcPath);
		return false;
	}

	try
	{
		if (src.extension() != ".zip")
		{
			LOG_ERROR("文件后缀并非.zip");
			return false;
		}
		ExtractZip(src, dest);
	}
	catch (const std::exception& e)
	{
		LOG_ERROR("解压失败: ", e.what());
		return false;
	}

	LOG_DEBUG("解压成功，压缩文件路径：", srcPath, "，解压路径：", destPath);
	return true;
}
std::vector<unsigned char> FileUtilPrivate::getFileBytes(const std::string& filePath)
{
	std::lock_guard<std::mutex> lock(m_fileLocker);
	std::ifstream file(filePath, std::ios::binary);
	if (!file)
	{
		LOG_ERROR("No such file: ", filePath);
		return {};
	}
	return {std::istreambuf_iterator<char>(file), {}};
}

bool FileUtilPrivate::isUrl(const std::string& url)
{
	try
	{
		static const std::regex urlRegex(
				// 1. 协议 http 或 https
				// 2. 主机名支持三类：localhost / 域名 / IPv4
				// 3. 允许端口号 :8080
				// 4. 路径、查询参数、fragment 都可选
				R"(^https?://((localhost)|((\d{1,3}\.){3}\d{1,3})|([\w\-]+\.)+[\w\-]+)(:\d+)?(/[ \w\-\./?%&=]*)?$)",
				std::regex::icase);
		return std::regex_match(url, urlRegex);
	}
	catch (const std::regex_error& e)
	{
		LOG_ERROR("Regex error: ", e.what());
		return false;
	}
}

void FileUtilPrivate::CompressFile(const std::filesystem::path& sourceFilePath, const std::filesystem::path& zipFilePath)
{
	int errorCode = 0;
	// zip_open创建并打开zip对象
	zip_t* zipArchive = zip_open(zipFilePath.generic_u8string().c_str(),
								 ZIP_CREATE | ZIP_TRUNCATE, &errorCode);
	if (zipArchive)
	{
		// 将被压缩项目加入到zip对象中
		AddFile2Zip(sourceFilePath, sourceFilePath.filename().string().c_str(),
					zipArchive);

		// 关闭zip，关闭时执行编码、打包、写入磁盘
		errorCode = zip_close(zipArchive);
		if (errorCode != 0)
		{
			zip_error_t zipError;
			zip_error_init_with_code(&zipError, errorCode);
			LOG_ERROR("压缩文件失败：", zip_error_strerror(&zipError));
			zip_error_fini(&zipError);
		}
	}
	else
	{
		zip_error_t zipError;
		zip_error_init_with_code(&zipError, errorCode);
		LOG_ERROR("打开输出文件失败: ", zipFilePath, ": ", zip_error_strerror(&zipError));
		zip_error_fini(&zipError);
	}
}

void FileUtilPrivate::CompressDirectory(const std::filesystem::path& directoryPath, const std::filesystem::path& zipFilePath)
{
	int errorCode = 0;
	zip_t* zipArchive = zip_open(zipFilePath.generic_u8string().c_str(),
								 ZIP_CREATE | ZIP_TRUNCATE, &errorCode);
	if (zipArchive)
	{
		AddDirectory2Zip(directoryPath, directoryPath, zipArchive);

		errorCode = zip_close(zipArchive);
		if (errorCode != 0)
		{
			zip_error_t zipError;
			zip_error_init_with_code(&zipError, errorCode);
			LOG_ERROR("压缩失败文件夹失败：", zip_error_strerror(&zipError));
			zip_error_fini(&zipError);
		}
	}
	else
	{
		zip_error_t zipError;
		zip_error_init_with_code(&zipError, errorCode);
		LOG_ERROR("打开输出文件失败: ", zipFilePath, ": ", zip_error_strerror(&zipError));
		zip_error_fini(&zipError);
	}
}

void FileUtilPrivate::AddFile2Zip(const std::filesystem::path& sourceFilePath, const char* relativeName, zip_t* zipArchive)
{
	std::ifstream file(sourceFilePath, std::ios::binary);
	file.seekg(0, std::ios::end);
	size_t bufferSize = file.tellg();
	char* bufferData = static_cast<char*>(malloc(bufferSize));

	file.seekg(0, std::ios::beg);
	file.read(bufferData, bufferSize);

	//第四个参数如果非0，会自动托管申请的资源，直到zip_close之前自动销毁。
	zip_source_t* source =
			zip_source_buffer(zipArchive, bufferData, bufferSize, 1);

	if (source)
	{
		if (zip_file_add(zipArchive, relativeName, source, ZIP_FL_OVERWRITE) < 0)
		{
			LOG_ERROR("添加文件：", sourceFilePath, " 到压缩包失败：", zip_strerror(zipArchive));
			zip_source_free(source);
		}
	}
	else
	{
		LOG_ERROR("创建zip源失败：", sourceFilePath, "：", zip_strerror(zipArchive));
	}
}
void FileUtilPrivate::AddDirectory2Zip(const std::filesystem::path& rootDirectoryPath, const std::filesystem::path& directoryPath, zip_t* zipArchive)
{
	if (rootDirectoryPath != directoryPath)
	{
		if (zip_dir_add(zipArchive,
						std::filesystem::relative(directoryPath, rootDirectoryPath)
								.generic_u8string()
								.c_str(),
						ZIP_FL_ENC_UTF_8) < 0)
		{
			LOG_ERROR("添加文件夹 ", directoryPath, " 到压缩包失败：", zip_strerror(zipArchive));
		}
	}

	for (const auto& entry: std::filesystem::directory_iterator(directoryPath))
	{
		if (entry.is_regular_file())
		{
			AddFile2Zip(
					entry.path().generic_u8string(),
					std::filesystem::relative(entry.path(), rootDirectoryPath)
							.generic_u8string()
							.c_str(),
					zipArchive);
		}
		else if (entry.is_directory())
		{
			AddDirectory2Zip(rootDirectoryPath, entry.path().generic_u8string(),
							 zipArchive);
		}
	}
}

void FileUtilPrivate::ExtractZip(const std::filesystem::path& zipFilePath, const std::filesystem::path& outputDir)
{
	int errorCode = 0;

	// 打开压缩文件
	zip_t* zipArchive =
			zip_open(zipFilePath.generic_u8string().c_str(), 0, &errorCode);

	if (!zipArchive)
	{
		zip_error_t zipError;
		zip_error_init_with_code(&zipError, errorCode);

		LOG_ERROR("打开压缩文件失败：", zip_error_strerror(&zipError));

		zip_error_fini(&zipError);
		return;
	}

	// 获取zip条目数量
	zip_int64_t numEntries = zip_get_num_entries(zipArchive, 0);
	for (zip_uint64_t i = 0; i < numEntries; i++)
	{
		zip_stat_t stat;
		if (zip_stat_index(zipArchive, i, 0, &stat) == 0)
		{
			std::filesystem::path outPath = outputDir / stat.name;

			// 通过条目名称判断是目录还是文件
			// 如果是目录（名称以 / 结尾），则在输出目录中创建对应目录；
			if (stat.name[strlen(stat.name) - 1] == '/')
			{
				// 目录
				std::filesystem::create_directories(outPath);
			}
			// 如果是文件，则创建其父目录，然后打开 ZIP 中的文件，读取内容并写入到输出路径；
			else
			{
				// 文件
				std::filesystem::create_directories(outPath.parent_path());

				zip_file_t* file = zip_fopen_index(zipArchive, i, 0);
				if (!file)
				{
					LOG_ERROR("在压缩包中打开文件失败：", stat.name);
					continue;
				}

				std::ofstream outFile(outPath, std::ios::binary);
				char buffer[8192];
				zip_int64_t bytesRead = 0;

				while ((bytesRead = zip_fread(file, buffer, sizeof(buffer))) > 0)
				{
					outFile.write(buffer, bytesRead);
				}

				zip_fclose(file);
			}
		}
	}

	zip_close(zipArchive);
}

QImage FileUtilPrivate::getFileIcon(const std::string& filePath)
{
	//1.如果文件是url，调用getIconFromUrl方法
	if (isUrl(filePath))
	{
		return getIconFromUrl(filePath);
	}
	//2.如果为系统文件,获取系统图标
	QString qFilePath = QString::fromStdString(filePath);
	QFileInfo fileInfo(qFilePath);
	if (!fileInfo.exists())
	{
		LOG_ERROR("文件不存在，无法获取系统图标: ", filePath);
		return {};
	}
	// 使用 QFileIconProvider 获取系统图标
	QFileIconProvider iconProvider;
	QIcon fileIcon = iconProvider.icon(fileInfo);
	// QIcon 转 QImage
	QPixmap pixmap = fileIcon.pixmap(ICON_SIZE);
	QImage image = pixmap.isNull() ? QImage() : pixmap.toImage();
	LOG_DEBUG("获取系统图标成功: ", filePath);
	return image;
}

QImage FileUtilPrivate::getImage(const std::string& filePath)
{
	QString qFilePath = QString::fromStdString(filePath);
	QFileInfo fileinfo(qFilePath);
	if (!fileinfo.exists())
	{
		LOG_ERROR("文件不存在: ", filePath);
		return {};
	}
	QImage image;
	bool loadSuccess = image.load(qFilePath);
	if (!loadSuccess)
	{
		LOG_ERROR("获取该文件图片失败: ", filePath);
		return {};
	}
	return image;
}
QImage FileUtilPrivate::getIconFromUrl(const std::string& url)
{
	// 1. 验证URL有效性
	if (!isUrl(url))
	{
		LOG_ERROR("无效的URL", url);
		return {};
	}
	// 2. 提取文件后缀名
	QUrl requestUrl(QString::fromStdString(url));
	QString path = requestUrl.path();
	QFileInfo fileInfo(path);
	QString suffix = fileInfo.suffix().toLower();// 统一转为小写处理
	// 3. 创建带后缀的临时文件模板
	QString templateName = QDir::tempPath() + "/qt_icon_XXXXXX";
	if (!suffix.isEmpty())
	{
		templateName += "." + suffix;
	}
	// 4. 创建临时文件并获取其图标
	QTemporaryFile tempFile(templateName);
	// 确保临时文件创建成功
	if (tempFile.open())
	{
		// 获取临时文件信息
		QFileInfo tempInfo(tempFile.fileName());
		// 获取系统关联图标
		QFileIconProvider iconProvider;
		QIcon icon = iconProvider.icon(tempInfo);
		// 转换为指定大小的图像
		// QIcon 转 QImage
		QPixmap pixmap = icon.pixmap(ICON_SIZE);
		QImage image = pixmap.isNull() ? QImage() : pixmap.toImage();
		LOG_DEBUG("获取url图标成功: ", url);
		return image;
	}
	else
	{
		LOG_ERROR("无法创建临时url文件", url);
		return {};
	}
}